 package com.jiawa.train.business.service;

 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.util.EnumUtil;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.jiawa.train.business.domain.*;
 import com.jiawa.train.business.enums.ConfirmOrderStatusEnum;
 import com.jiawa.train.business.enums.SeatColEnum;
 import com.jiawa.train.business.enums.SeatTypeEnum;
 import com.jiawa.train.business.mapper.ConfirmOrderMapper;
 import com.jiawa.train.business.mapper.DailyTrainTicketMapper;
 import com.jiawa.train.business.req.ConfirmOrderDoReq;
 import com.jiawa.train.business.req.ConfirmOrderQueryReq;
 import com.jiawa.train.business.req.ConfirmOrderTicketReq;
 import com.jiawa.train.business.resp.ConfirmOrderQueryResp;
 import com.jiawa.train.common.aspect.LogAspect;
 import com.jiawa.train.common.context.LoginMemberContext;
 import com.jiawa.train.common.exception.BusinessException;
 import com.jiawa.train.common.exception.BusinessExceptionEnum;
 import com.jiawa.train.common.resp.PageResp;
 import com.jiawa.train.common.util.SnowUtil;
 import jakarta.annotation.Resource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;

 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;

@Service
public class ConfirmOrderService {

    private static final Logger LOG = LoggerFactory.getLogger(ConfirmOrderService.class);

    @Resource
    private ConfirmOrderMapper confirmOrderMapper;

    @Autowired
    private DailyTrainTicketService dailyTrainTicketService;
    @Autowired
    private DailyTrainCarriageService dailyTrainCarriageService;
    @Autowired
    private DailyTrainSeatService dailyTrainSeatService;

    @Resource
    private DailyTrainTicketMapper dailyTrainTicketMapper;
	@Autowired
	private LogAspect logAspect;
    @Autowired
    private AfterConfirmOrderService afterConfirmOrderService;

    public void doConfirm(ConfirmOrderDoReq req) throws Exception {
        //省略业务数据校验
        //一个订单里可能有多张车票，获取当前订单中所有乘客的车票列表
        List<ConfirmOrderTicketReq> tickets = req.getTickets();
        //保存确认订单表，状态初始
        DateTime now = DateTime.now();
        ConfirmOrder confirmOrder = new ConfirmOrder();
        confirmOrder.setId(SnowUtil.getSnowflakeNextId());
        confirmOrder.setMemberId(LoginMemberContext.getId());

        Date date = req.getDate();
        String trainCode = req.getTrainCode();
        String start = req.getStart();
        String end = req.getEnd();

        confirmOrder.setDate(date);
        confirmOrder.setTrainCode(trainCode);
        confirmOrder.setStart(start);
        confirmOrder.setEnd(end);
        confirmOrder.setDailyTrainTicketId(req.getDailyTrainTicketId());
        confirmOrder.setStatus(ConfirmOrderStatusEnum.INIT.getCode());
        confirmOrder.setCreateTime(now);
        confirmOrder.setUpdateTime(now);
        confirmOrder.setTickets(JSON.toJSONString(req.getTickets()));
        confirmOrderMapper.insert(confirmOrder);

        //查出余票记录，需要得到真实的库存
        DailyTrainTicket dailyTrainTicket = dailyTrainTicketService.selectByUnique(date, trainCode, start, end);
        LOG.info("查出余票记录: {}", dailyTrainTicket);
        //扣减余票数量，并判断余票是否足够
        reduceTickets(req, dailyTrainTicket);

        //最终的选座结果
        List<DailyTrainSeat> finalSeatList = new ArrayList<>();

        //计算相对第一个座位的偏移值
        //比如选择的是C1,02，则偏移值是:[0,5]
        //比如选择的是A1,B1,C1，则偏移值是:[0,1,2]
        List<String> referSeatList = new ArrayList<>();
        ConfirmOrderTicketReq ticketReq0 = tickets.get(0);
        //只要判断其中一个乘客有没有选座即可，要么都选，要么都不选
        if(StrUtil.isNotBlank(ticketReq0.getSeat())){
            LOG.info("本次购票有选座");
            List<SeatColEnum> colEnumList = SeatColEnum.getColsByType(ticketReq0.getSeatTypeCode());
            LOG.info("本次选座的座位类型包含: "+colEnumList);
            for(int i = 1; i <= 2;i++){
                for(SeatColEnum seatColEnum : colEnumList){
                    referSeatList.add(seatColEnum.getCode()+i);
                }
            }
            List<Integer> offsetList = new ArrayList<>();
            List<Integer> aboluteOffsetList = new ArrayList<>();
            for(ConfirmOrderTicketReq ticketReq : tickets){
                int index = referSeatList.indexOf(ticketReq.getSeat());
                aboluteOffsetList.add(index);
            }
            LOG.info("计算得到所有座位的绝对偏移值：{}",aboluteOffsetList);
            for(Integer index : aboluteOffsetList){
                int offset = index - aboluteOffsetList.get(0);
                offsetList.add(offset);
            }
            LOG.info("计算得到所有座位的相对第一个座位的偏移值：{}",offsetList);
            getSeat(finalSeatList, date,trainCode, ticketReq0.getSeatTypeCode(), ticketReq0.getSeat().split("")[0],offsetList,
                    dailyTrainTicket.getStartIndex(),dailyTrainTicket.getEndIndex());
        }else{
            LOG.info("本次购票无选座");
            for(ConfirmOrderTicketReq ticketReq : tickets){
                getSeat(finalSeatList,date,trainCode, ticketReq0.getSeatTypeCode(), null,null,
                        dailyTrainTicket.getStartIndex(),dailyTrainTicket.getEndIndex());
            }

        }
        LOG.info("最终选座:{} ",finalSeatList);

        //选中座位后事务处理：
            //座位表修改售卖情况sell
            //余票详情票修改余票
            //为会员增加购票记录
            //更新确认订单表为成功
        afterConfirmOrderService.afterDoConfirm(dailyTrainTicket,finalSeatList,tickets,confirmOrder);
    }
    private void getSeat(List<DailyTrainSeat> finalSeatList, Date date, String trainCode, String seatType, String column, List<Integer> offsetList,
                         Integer startIndex, Integer endIndex){
        List<DailyTrainSeat> getSeatList = new ArrayList<>();
        List<DailyTrainCarriage> carriageList = dailyTrainCarriageService.selectBySeatType(date, trainCode, seatType);
        LOG.info("共查出{}个符合条件的车厢",carriageList.size());
        //一个车厢一个车厢的获取座位数据
        for(DailyTrainCarriage dailyTrainCarriage : carriageList){
            LOG.info("开始从车厢{}选座",dailyTrainCarriage.getIndex());
            getSeatList = new ArrayList<>();
            List<DailyTrainSeat> seatList = dailyTrainSeatService.selectByCarriage(date, trainCode, dailyTrainCarriage.getIndex());
            LOG.info("车厢{}的座位数:{}",dailyTrainCarriage.getIndex(),seatList.size());
	        for (int i = 0; i < seatList.size(); i++) {
		        DailyTrainSeat dailyTrainSeat = seatList.get(i);
                String col = dailyTrainSeat.getCol();
                Integer seatIndex = dailyTrainSeat.getCarriageSeatIndex();
//                判断当前座位不能被选中过
                boolean alreadyFlag = false;
                for(DailyTrainSeat finalSeat : finalSeatList){
                    if(finalSeat.getId().equals(dailyTrainSeat.getId())){
                        alreadyFlag = true;
                        break;
                    }
                }
                if(alreadyFlag){
                    LOG.info("座位{}被选中过，不能重复选中，继续判断下一个座位",seatIndex);
                   continue;
                }
		        //判断column，有值的话要比对列号

		        if (StrUtil.isBlank(column)) {
			        LOG.info("未选中座位");
		        } else {
			        if (!column.equals(col)) {
				        LOG.info("座位{}列值不对，继续判断下一个座位，当前列值{}，目标列值{}", seatIndex, col, column);
				        continue;
			        }
		        }
		        Boolean isChoose = calSell(dailyTrainSeat, startIndex, endIndex);
		        if (isChoose) {
			        LOG.info("选中座位");
                    getSeatList.add(dailyTrainSeat);
		        } else {
			        continue;
		        }
		        //根据offset继续选剩下的座位

		        //是否已经选中了所有的offerset
		        boolean isGetAllOffsetSeat = true;
		        if (CollUtil.isNotEmpty(offsetList)) {
			        LOG.info("有偏移值: {},校验偏的座位是否可选", offsetList);
			        //从索引1开始，索引0是当前已选中的票
			        for (int j = 1; j < offsetList.size(); j++) {
				        Integer offset = offsetList.get(j);
				        //座位在库的索引是从1开始
//                        int nextIndex = seatIndex + offset - 1;
                        int nextIndex = i + offset;
				        //有选座时，一定是在同一个车厢
				        if (nextIndex >= seatList.size()) {
					        LOG.info("座位{}不可选,偏移后的索引超过了这个车厢的座位数", nextIndex);
					        isGetAllOffsetSeat = false;
					        break;
				        }
				        DailyTrainSeat nextDailyTrainSeat = seatList.get(nextIndex);
				        boolean isChooseNext = calSell(nextDailyTrainSeat, startIndex, endIndex);
				        if (isChooseNext) {
					        LOG.info("座位{}被选中", nextDailyTrainSeat.getCarriageSeatIndex());
                            getSeatList.add(nextDailyTrainSeat);
				        } else {
					        LOG.info("座位{}不可选", nextDailyTrainSeat.getCarriageSeatIndex());
					        isGetAllOffsetSeat = false;
					        break;
				        }
			        }
		        }
		        if (!isGetAllOffsetSeat) {
                    getSeatList = new ArrayList<>();
			        continue;
		        }
		        //保存选好的座位
                finalSeatList.addAll(getSeatList);
		        return;
	        }
        }

    }
    //计算座位在某区间是否可卖
    //例:sell=10001,若本次购买区间站是1~4, 则截取区间已售000
    //全部是0，表示这个区间可买;只要有1，就表示区间内已售过票

//    选中后，要计算购票后的sell，比如原来是10001，本次购买区间站1~4
//    方案:构造本次购票造成的售卖信息01110，和原sell 10001按位或最终得到11111
    public Boolean calSell(DailyTrainSeat dailyTrainSeat,Integer startIndex,Integer endIndex){
        //10001
        String sell = dailyTrainSeat.getSell();
        //000
        String sellPart = sell.substring(startIndex, endIndex);
        if(Integer.parseInt(sellPart) > 0){
            LOG.info("座位{} 在本次车站区间{}~{}已售过票，不可选中该座位",dailyTrainSeat.getCarriageIndex(),startIndex,endIndex);
            return false;
        }else{
            LOG.info("座位{} 在本次车站区间{}~{}未售过票，可选中该座位",dailyTrainSeat.getCarriageIndex(),startIndex,endIndex);
            //111
            String curSell = sellPart.replace('0', '1');
            //0111
            curSell = StrUtil.fillBefore(curSell,'0',endIndex);
            //01110
            curSell = StrUtil.fillAfter(curSell,'0',sell.length());

            //当前区间售票信息curSell与库里的已售信息sell按位或，即可得到该座位卖出此票后的售票详情
            //32
            int newSellInt = NumberUtil.binaryToInt(curSell) | NumberUtil.binaryToInt(sell);
            //32 -> 11111
            String newSell = NumberUtil.getBinaryStr(newSellInt);
            //可能的特殊情况处理（如二进制串01111，转成整数后为16，再转成二进制为1111，丢失了最前面的0，因此需要在前面补0）
            curSell = StrUtil.fillBefore(curSell,'0',sell.length());
            LOG.info("座位{}被选中,原售票信息:{}.车站区间:{}~{},即︰{},最终售票信息∶{}"
                    , dailyTrainSeat.getCarriageSeatIndex()
                    , sell, startIndex,endIndex,curSell,newSell);
            dailyTrainSeat.setSell(newSell);
            return true;
        }
    }
    private static void reduceTickets(ConfirmOrderDoReq req, DailyTrainTicket dailyTrainTicket) {
        for(ConfirmOrderTicketReq ticketReq : req.getTickets()) {
            String seatTypeCode = ticketReq.getSeatTypeCode();
            SeatTypeEnum seatTypeEnum = EnumUtil.getBy(SeatTypeEnum::getCode, seatTypeCode);
            switch (seatTypeEnum) {
                case YDZ -> {
                    int countLeft = dailyTrainTicket.getYdz() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYdz(countLeft);
                }
                case EDZ -> {
                    int countLeft = dailyTrainTicket.getEdz() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setEdz(countLeft);
                }
                case RW -> {
                    int countLeft = dailyTrainTicket.getRw() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setRw(countLeft);
                }
                case YW -> {
                    int countLeft = dailyTrainTicket.getYw() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYw(countLeft);
                }
            }

        }

    }

    public PageResp<ConfirmOrderQueryResp> queryList(ConfirmOrderQueryReq req) {
        ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
        confirmOrderExample.setOrderByClause("id desc");
        ConfirmOrderExample.Criteria criteria = confirmOrderExample.createCriteria();

        LOG.info("查询页码：{}", req.getPage());
        LOG.info("每页条数：{}", req.getSize());
        PageHelper.startPage(req.getPage(), req.getSize());
        List<ConfirmOrder> confirmOrderList = confirmOrderMapper.selectByExample(confirmOrderExample);

        PageInfo<ConfirmOrder> pageInfo = new PageInfo<>(confirmOrderList);
        LOG.info("总行数：{}", pageInfo.getTotal());
        LOG.info("总页数：{}", pageInfo.getPages());

        List<ConfirmOrderQueryResp> list = BeanUtil.copyToList(confirmOrderList, ConfirmOrderQueryResp.class);

        PageResp<ConfirmOrderQueryResp> pageResp = new PageResp<>();
        pageResp.setTotal(pageInfo.getTotal());
        pageResp.setList(list);
        return pageResp;
    }

    public void delete(Long id) {
        confirmOrderMapper.deleteByPrimaryKey(id);
    }
}
